<?php
// CLASS constants
define('TABLE_DATA', 'data');
define('FIELD_LEFT_ID', 'left_id');
define('FIELD_RIGHT_ID', 'right_id');
define('FIELD_LEVEL', 'level');
define('FIELD_KEY', 'id');

class mptt
{
	/**
	 * MPTT (Modified Pre-ordered Tree Traversal) Class.
	 * Only for tree maintence (no methods for printing the tree and such).
	 * Class selects needed data from the database.
	 *
	 * Make sure you have set the constants:
	 *  TABLE_DATA - The name of the table in which the tree data is stored.
	 *  FIELD_KEY - The name of the PRIMARY KEY column (the one with row id's).
	 *  FIELD_LEFT_ID - The name of the column in which the left id's are stored.
	 *  FIELD_RIGHT_ID - The name of the column in which the right id's are stored.
	 *  FIELD_LEVEL - The name of the column in which the elemts levels are stored.
	 *
	 * All 4 columns are absolutely REQUIRED for the class to function.
	 *
	 * @author marek_mar
	 *
	 * @version Second Release
	 *
	 * @todo Find some more bugs...
	 */

	/**
	 * @var MySQL resource
	 */

	// Normal constants used as there are no class constants in PHP4.
	var $link;

	// Public methods

	/**
	 * Constructor.
	 *
	 * @param (optional) MySQL resource $link
	 * @return bool(true)
	 */
	function mptt($link = null)
	{
		$this->link = $link;
		return true;
	}

	/**
	 * Add an element to the tree as a child of $parent and as $child_num'th child. If $data is not supplied the insert id will be returned.
	 *
	 * @param int $parent
	 * @param int $child_num
	 * @param array $misc_data
	 * @return bool or int
	 */
	function add($parent, $child_num = 0, $misc_data = false)
	{
		if(!is_numeric($parent) || $parent < 0)
		{
			return false;
		}
		if($parent != 0)
		{
			$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $parent . ';';
			$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
			if(mysql_num_rows($result) != 1)
			{ // Row must exist.
				return false;
			}
			$parent = mysql_fetch_assoc($result);
			mysql_free_result($result);
		}
		else
		{
			// Virtual root element as parent.
			$parent = $this->get_virtual_root();
		}
		$children = $this->get_children($parent['left_id'], $parent['right_id'], $parent['level']);

		if(count($children) == 0)
		{
			$child_num = 0;
		}
		if($child_num == 0 || (count($children) - $child_num) <= 0 || (count($children) + $child_num + 1) < 0)
		{
			$boundry = array(FIELD_LEFT_ID, FIELD_RIGHT_ID, $parent['left_id']);
		}
		elseif($child_num != 0)
		{
			// Some other child.
			if($child_num < 0)
			{
				$child_num = count($children) + $child_num + 1;
			}
			if($child_num > count($children))
			{
				$child_num = count($children);
			}
			$boundry = array(FIELD_RIGHT_ID, FIELD_LEFT_ID, $children[$child_num - 1]['right_id']);
		}
		else
		{
			return false;
		}

		//We have what we want.
		$sql = array();

		// Make a hole for the new element.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` + 2 WHERE `' . $boundry[0] . '` > ' . $boundry[2] . ' AND `' . $boundry[1] . '` > ' . $boundry[2] . ';';
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` + 2 WHERE `' . $boundry[1] . '` > ' . $boundry[2] . ';';
		// Insert the new element.

		$data = array(
			FIELD_LEFT_ID => $boundry[2] + 1,
			FIELD_RIGHT_ID => $boundry[2] + 2,
			FIELD_LEVEL => $parent['level'] + 1
		);
		if($misc_data && is_array($misc_data))
		{
			$data = array_merge($misc_data, $data);
			$data = $this->build_sql($data);
		}
		$sql[] = 'INSERT INTO `' . TABLE_DATA . '` SET ' . $data . ';';

		// Now we have to run the SQL.
		$this->exec_sql($sql);

		if(!$misc_data)
		{
			return mysql_insert_id($this->link);
		}
		return true;


	}

	/**
	 * Deletes element $id with or without children. If children should be kept they will become children of $id's parent.
	 *
	 * @param int $id
	 * @param bool $keep_children
	 * @return bool
	 */
	function delete($id, $keep_children = false)
	{
		if(!is_numeric($id) || $id <= 0 || !is_bool($keep_children))
		{
			return false;
		}
		$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id . ';';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		if(mysql_num_rows($result) != 1)
		{ // Row must exist.
			return false;
		}
		$a = mysql_fetch_assoc($result);

		$sql = array();

		if(!$keep_children)
		{
			// Delete the element with children.
			$sql[] = 'DELETE FROM `' . TABLE_DATA . '` WHERE `' . FIELD_LEFT_ID . '` >= ' . $a['left_id'] . ' AND `' . FIELD_RIGHT_ID . '` <= ' . $a['right_id'] . ';';
			// Remove the hole.
			$diff = $a['right_id'] - $a['left_id'] + 1;
			$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` - ' . $diff . ' WHERE `' . FIELD_RIGHT_ID . '` > ' . $a['right_id'] . ' AND `' . FIELD_LEFT_ID . '` > ' . $a['right_id'] . ';';
			$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` - ' . $diff . ' WHERE `' . FIELD_RIGHT_ID . '` > ' . $a['right_id'] . ';';
			// No level cahnges needed.
		}
		else
		{
			// Delete ONLY the element.
			$sql[] = 'DELETE FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id . ';';
			// Fix children.
			$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` - 1, `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` - 1, `' . FIELD_LEVEL . '` = `' . FIELD_LEVEL . '` - 1 WHERE `' . FIELD_LEFT_ID . '` >= ' . $a['left_id'] . ' AND `' . FIELD_RIGHT_ID . '` <= ' . $a['right_id'] . ';';
			// Remove hole.
			$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` - 2 WHERE `' . FIELD_RIGHT_ID . '` > ' . ($a['right_id'] - 1) . ' AND `' . FIELD_LEFT_ID . '` > ' . ($a['right_id'] - 1) . ';';
			$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` - 2 WHERE `' . FIELD_RIGHT_ID . '` > ' . ($a['right_id'] - 1) . ';';
		}

		// Run SQL.
		$this->exec_sql($sql);
	}

	/**
	 * Move a element (with children) $id, under element $target_id as the $child_num'th child of that element
	 *
	 * @param int $id
	 * @param int $target_id
	 * @param int $child_num
	 * @return bool
	 */
	function move($id, $target_id, $child_num = 0)
	{
		if(!is_numeric($id) || !is_numeric($target_id) || !is_numeric($child_num))
		{
			return false;
		}
		if($target_id != 0)
		{
			$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id . ' OR `' . FIELD_KEY . '` = ' . $target_id;
				// I want the to be returned in order.
				$sql .= ' ORDER BY `id` ' . (($id < $target_id) ? 'ASC' : 'DESC') . ';';

			$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
			if(mysql_num_rows($result) != 2)
			{ // Both rows must exist.
				return false;
			}
			$a = mysql_fetch_assoc($result); // This is being moved.
			$b = mysql_fetch_assoc($result); // This is the target.
		}
		else
		{
			$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id . ';';

			$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
			if(mysql_num_rows($result) != 1)
			{ // Row must exist.
				return false;
			}
			$a = mysql_fetch_assoc($result); // This is being moved.

			// Virtual root element.
			$b = $this->get_virtual_root();
		}

		mysql_free_result($result);

		// We need to get the children.
		$children = $this->get_children($b['left_id'], $b['right_id'], $b['level']);

		if(count($children) == 0)
		{
			$child_num = 0;
		}
		if($child_num == 0 || (count($children) - $child_num) <= 0 || (count($children) + $child_num + 1) < 0)
		{
			// First child.
			$boundry = array(FIELD_LEFT_ID, FIELD_RIGHT_ID, FIELD_RIGHT_ID, $b['left_id']);
		}
		elseif($child_num != 0)
		{
			// Some other child.
			if($child_num < 0)
			{
				$child_num = count($children) + $child_num + 1;
			}
			if($child_num > count($children))
			{
				$child_num = count($children);
			}
			$boundry =  array(FIELD_RIGHT_ID, FIELD_LEFT_ID, FIELD_RIGHT_ID, $children[$child_num - 1]['right_id']);
		}
		else
		{
			return false;
		}


		// Math.
		$diff = $a['right_id'] - $a['left_id'] + 1; // The "size" of the tree.

		if($a['left_id'] < $boundry[3])
		{
			$size = $boundry[3] - $diff;
			$dist = $boundry[3] - $diff - $a['left_id'] + 1;
		}
		else
		{
			$size = $boundry[3];
			$dist = $boundry[3] - $a['left_id'] + 1;
		}
		// Level math.
		$ldiff = ($a['level'] - $b['level'] - 1) * -1;
		// We have all what we need.


		$sql = array();

		// Give the needed rows negative id's.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` * -1, `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` * -1 WHERE `' . FIELD_LEFT_ID . '` >= ' . $a['left_id'] . ' AND `' . FIELD_RIGHT_ID . '` <= ' . $a['right_id'] . ';';
		// Remove the hole.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` - ' . $diff . ' WHERE `' . FIELD_RIGHT_ID . '` > ' . $a['right_id'] . ' AND `' . FIELD_LEFT_ID . '` > ' . $a['right_id'] . ';';
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` - ' . $diff . ' WHERE `' . FIELD_RIGHT_ID . '` > ' . $a['right_id'] . ';';
		// Add hole
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` + ' . $diff . ' WHERE `' . $boundry[0] . '` > ' . $size . ' AND `' . $boundry[1] . '` > ' . $size . ';';
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` + ' . $diff . ' WHERE `' . $boundry[2] . '` > ' . $size . ';';
		// Fill hole & update rows & multiply by -1
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = (`' . FIELD_LEFT_ID . '` - (' . $dist . ')) * -1, `' . FIELD_RIGHT_ID . '` = (`' . FIELD_RIGHT_ID . '` - (' . $dist . ')) * -1, `' . FIELD_LEVEL . '` = `' . FIELD_LEVEL . '` + (' . $ldiff . ') WHERE `' . FIELD_LEFT_ID . '` < 0;';


		// Now we need to execute the queries.
		$this->exec_sql($sql);
		return true;
	}


	/**
	 * Copies element $id (with children) to $parent as the $child_mun'th child.
	 *
	 * @param int $id
	 * @param int $parent
	 * @param int $child_num
	 * @return bool
	 */
	function copy($id, $parent, $child_num = 0)
	{
		if(!is_numeric($id) || $id < 0 ||!is_numeric($parent) || $parent < 0)
		{
			return false;
		}

		// Get branch left & right id's.
		$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id . ';';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		if(mysql_num_rows($result) != 1)
		{ // Row must Exist.
			return false;
		}
		$a = mysql_fetch_assoc($result);
		mysql_free_result($result);
		// Get child data.
		$sql = 'SELECT * FROM `' . TABLE_DATA . '` WHERE `' . FIELD_LEFT_ID . '` >= ' . $a['left_id'] . ' AND `' . FIELD_RIGHT_ID . '` <= ' . $a['right_id'] . ';';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		while($row = mysql_fetch_assoc($result))
		{
			$data[] = $row;
		}


		if($parent != 0)
		{
			$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $parent . ';';

			$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
			if(mysql_num_rows($result) != 1)
			{ // Row must exist.
				return false;
			}
			$b = mysql_fetch_assoc($result);
		}
		else
		{
			$b = $this->get_virtual_root();
		}

		// Get target's children.
		$children = $this->get_children($b['left_id'], $b['right_id'], $b['level']);

		if(count($children) == 0)
		{
			$child_num = 0;
		}
		if($child_num == 0 || (count($children) - $child_num) <= 0 || (count($children) + $child_num + 1) < 0)
		{
			// First child.
			$boundry = array(FIELD_LEFT_ID, FIELD_RIGHT_ID, FIELD_RIGHT_ID, $b['left_id']);
		}
		elseif($child_num != 0)
		{
			// Some other child.
			if($child_num < 0)
			{
				$child_num = count($children) + $child_num + 1;
			}
			if($child_num > count($children))
			{
				$child_num = count($children);
			}
			$boundry =  array(FIELD_RIGHT_ID, FIELD_LEFT_ID, FIELD_RIGHT_ID, $children[$child_num - 1]['right_id']);
		}
		else
		{
			return false;
		}

		// Math.
		$diff = $a['right_id'] - $a['left_id'] + 1;
		$dist = $boundry[3] - $a['left_id'] + 1;
		// Level math.
		$ldiff = ($a['level'] - $b['level'] - 1);

		// We have all we need.
		$sql = array();

		// Add hole.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = `' . FIELD_LEFT_ID . '` + ' . $diff . ' WHERE `' . $boundry[0] . '` > ' . $boundry[3] . ' AND `' . $boundry[1] . '` > ' . $boundry[3] . ';';
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_RIGHT_ID . '` = `' . FIELD_RIGHT_ID . '` + ' . $diff . ' WHERE `' . $boundry[2] . '` > ' . $boundry[3] . ';';

		// Now we have to insert all the new elements.
		for($i = 0, $n = count($data); $i< $n; $i++)
		{
			// We need a new key.
			unset($data[$i][FIELD_KEY]);

			// This fields need new values.
			$data[$i][FIELD_LEFT_ID] += $dist;
			$data[$i][FIELD_RIGHT_ID] += $dist;
			$data[$i][FIELD_LEVEL] -= $ldiff;

			$data[$i] = $this->build_sql($data[$i]);
			$sql[] = 'INSERT INTO `' . TABLE_DATA . '` SET ' . $data[$i];
		}

		// Run SQL
		$this->exec_sql($sql);
		return true;
	}

	/**
	 * Swaps two elements and ONLY the elements!
	 *
	 * @param int $id1
	 * @param int $id2
	 * @return bool
	 */
	function swap($id1, $id2)
	{
		if(!is_numeric($id1) || $id1 <= 0 || !is_numeric($id2) || $id2 <= 0)
		{
			return false;
		}
		$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id`, `' . FIELD_LEVEL . '` AS `level` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_KEY . '` = ' . $id1 . ' OR `' . FIELD_KEY . '` = ' . $id2;
				// I want the to be returned in order.
				$sql .= ' ORDER BY `id` ' . (($id1 < $id2) ? 'ASC' : 'DESC') . ';';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		if(mysql_num_rows($result) != 2)
		{ // Both rows must exist.
			return false;
		}
		$a = mysql_fetch_assoc($result); // This is being moved.
		$b = mysql_fetch_assoc($result); // This is the target.

		$sql = array();
		// Swap a with b.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = ' . $b['left_id'] . ', `' . FIELD_RIGHT_ID . '` = ' . $b['right_id'] . ', `' . FIELD_LEVEL . '` = ' . $b['level'] . ' WHERE `' . FIELD_KEY . '` = ' . $id1 . ';';
		// Swap b with a.
		$sql[] = 'UPDATE `' . TABLE_DATA . '` SET `' . FIELD_LEFT_ID . '` = ' . $a['left_id'] . ', `' . FIELD_RIGHT_ID . '` = ' . $a['right_id'] . ', `' . FIELD_LEVEL . '` = ' . $a['level'] . ' WHERE `' . FIELD_KEY . '` = ' . $id2 . ';';

		// Now we need to execute the queries.
		$this->exec_sql($sql);
		return true;
	}

	/**
	 * Check if all of the tree's left and right id's are correct.
	 *
	 * @param array $errors
	 * @return bool
	 */
	function check_consistency(&$errors = array())
	{
		$sql = 'SELECT * FROM `' . TABLE_DATA . '` ORDER BY `' . FIELD_LEFT_ID . '`;';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		if(mysql_num_rows($result) == 0)
		{ // No tree. It should be consistant.
			return true;
		}

		$data = array();
		$ids = array();

		while($row = mysql_fetch_assoc($result))
		{
			$data[] = $row;
			$ids[] = $row[FIELD_LEFT_ID];
			$ids[] = $row[FIELD_RIGHT_ID];
		}

		rsort($ids, SORT_NUMERIC);
		$element_count = $ids[0] / 2;
		if(is_float($element_count))
		{ // If your first element has left_id=1 the last_right id must be even.
			$errors[] = 'Last right id is odd.';
			$element_count = ceil($element_count);
		}
		if(count($data) != ($ids[0] / 2))
		{ // It should be equal.
			$errors[] = 'Missing elements.';
			// Let's find the missing ones.
		}

		for($i = 0, $n = count($data); $i < $n; $i++)
		{
			// Elemnts left id & right id should never be both even or both odd.
			// This is from observation. If you have something against it I'll be happy to know.
			if(!(($data[$i][FIELD_LEFT_ID] % 2) xor ($data[$i][FIELD_RIGHT_ID] % 2)))
			{
				$errors[] = 'Element with the id: ' . $data[$i][FIELD_KEY] . ' has invalid left/right id\'s.';
			}
			// The left id should always be smaller than the right id.
			if($data[$i][FIELD_LEFT_ID] > $data[$i][FIELD_RIGHT_ID])
			{
				$errors[] = 'Element with the id: ' . $data[$i][FIELD_KEY] . ' has has too big left id.';
			}
		}

		// Elements might not be missing and have correct left&right id values but they might be off anyway (Two elements with the same id's for example).
		$id_sum = (1 + $ids[0]) * count($data);
		if(array_sum($ids) != $id_sum)
		{
			$errors[] = 'The sum of all left and right id\'s doesn\'t match the sum of left and right id\'s of the elements found.';
		}

		if(count($errors) > 0)
		{ // Let's check which left id's are missing.
			sort($ids, SORT_NUMERIC);
			for($i = 1, $n = count($ids); $i < $n; $i++)
			{
				var_dump($i);
				if($i != $ids[$i - 1])
				{
					$errors[] = 'Missing left/right id: ' . $i;
				}
			}
var_dump($ids);
			// Tree is not consistant.
			return false;
		}
		// Tree is consistant.
		return true;
	}


	// Private methods
	// Errors coused by using these methods by yourself are not considered errors or bugs.

	/**
	 * Should be a private method. Do not use.
	 *
	 * @param int $left_id
	 * @param int $right_id
	 * @param int $level
	 * @return array
	 */
	function get_children($left_id, $right_id, $level)
	{
		$sql = 'SELECT `' . FIELD_LEFT_ID . '` AS `left_id`, `' . FIELD_RIGHT_ID . '` AS `right_id` FROM `' . TABLE_DATA . '` WHERE `' . FIELD_LEFT_ID . '` > ' . $left_id . ' AND `' . FIELD_RIGHT_ID . '` < ' . $right_id . ' AND `' . FIELD_LEVEL . '` = ' . ($level + 1) . ';';
		$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);
		$children = array();
		while($child = mysql_fetch_assoc($result))
		{
			$children[] = $child;
		}
		mysql_free_result($result);

		return $children;
	}

	/**
	 * Return the left_id, right_id and level for the virtual root node.
	 *
	 * @return array
	 */
	function get_virtual_root()
	{
			// Virtual root element as parent.
			$sql = 'SELECT `' . FIELD_RIGHT_ID . '` AS `right_id` FROM `' . TABLE_DATA . '` ORDER BY `' . FIELD_RIGHT_ID . '` DESC LIMIT 1;';
			$result = mysql_query($sql, $this->link) or die(mysql_error() . "<br>\nSQL<br>\n" . $sql);

			$root = array('left_id' => 0, 'right_id' => mysql_fetch_assoc($result), 'level' => 0);
			$root['right_id'] = $root['right_id']['right_id'] + 1;
			mysql_free_result($result);
			return $root;
	}

	/**
	 * Executes multiple query.
	 *
	 * @param array $sql
	 */
	function exec_sql($sql)
	{
		for($i = 0, $n = count($sql); $i < $n; $i++)
		{
			mysql_query($sql[$i], $this->link) or die(mysql_error() . "<br />\nSQL: $i<br />\n" . $sql[$i]);
/*		// DEBUG:
		print 'STEP ' . $i . "<br />\n";
		$debug = 'SELECT * FROM `' . DATA_TABLE . '` ORDER BY `left_id` ASC';

		$raw_result = mysql_query($debug) or die(mysql_error() . '');

		while($item = mysql_fetch_array($raw_result))
		{
		    print '<div style="margin-left: ' . $item['level'] . 'em;">' . $item['id'] . ' | ' . $item['title'] . ' - ' . $item['level'] . ' - {' . $item['left_id'] . ', ' . $item['right_id'] . '}</div>' . "\n";
		}
*/
		}
	}

	/**
	 * Build INSERT statement
	 *
	 * @param array $data
	 * @return array
	 */
	function build_sql($data)
	{
		foreach($data as $k => $v)
		{
			if(is_numeric($v))
			{
				$data[$k] = '`' . $k . '` = ' . $v . '';
			}
			else
			{
				$data[$k] = '`' . $k . '` = \'' . mysql_real_escape_string($v) . '\'';
			}
		}
		return implode(', ', $data);
	}
}
?>